package com.heima.schedule.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.heima.common.contatns.RedisConstants;
import com.heima.common.contatns.ScheduleConstants;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskService;
import com.heima.utils.common.BeanHelper;
import com.heima.utils.common.JsonUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
public class TaskServiceImpl implements TaskService {
    @Autowired
    private TaskinfoMapper taskinfoMapper;
    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Long addTask(Task task) {
        //添加DB的任务
        boolean flag = addTaskToDb(task);

        //添加Redis的任务
        if(flag){
            addTaskToCache(task);
        }

        return task.getTaskId();
    }

    /**
     * 把任务添加到缓存
     * @param task
     */
    private void addTaskToCache(Task task) {
        //判断该任务的执行时间是否在5分钟以内，在的话才加入redis缓存

        //获取未来5分钟的时间
        long futureDate = DateTime.now().plusMinutes(5).getMillis();

        //判断任务的执行时间 是否小于或等于 未来5分钟的时间
        if(task.getExecuteTime()<=futureDate){
            //添加缓存中
            String key = RedisConstants.TASK_TOPIC_PREFIX+task.getTaskTopic();
            String taskJson = JsonUtils.toString(task);
            redisTemplate.opsForZSet().add(key,taskJson,task.getExecuteTime());
        }

    }

    /**
     * 把任务到MySQL数据库
     * @param task
     * @return
     */
    private boolean addTaskToDb(Task task) {
        boolean flag = false;
        try {
            //添加任务表
            Taskinfo taskinfo = BeanHelper.copyProperties(task,Taskinfo.class);
            taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
            taskinfoMapper.insert(taskinfo);

            //添加任务日志表
            TaskinfoLogs taskinfoLogs = BeanHelper.copyProperties(taskinfo,TaskinfoLogs.class);
            taskinfoLogs.setVersion(1);//设置乐观锁的初始版本号
            taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);
            taskinfoLogsMapper.insert(taskinfoLogs);

            //注意：给Task的taskId进行赋值
            task.setTaskId(taskinfo.getTaskId());

            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return flag;
    }


    @Override
    public List<Task> pollTask(Integer taskTopic) {
        List<Task> taskList = new ArrayList<>();

        //根据任务主题查询需要被执行的任务数据（时间到了）
        String key = RedisConstants.TASK_TOPIC_PREFIX+taskTopic;
        Set<String> taskSet = redisTemplate.opsForZSet().rangeByScore(key, 0, System.currentTimeMillis());

        //封装数据并返回，删除任务
        if(CollectionUtils.isNotEmpty(taskSet)){
            for(String taskJson:taskSet){
                //转换成Task对象
                Task task = JsonUtils.toBean(taskJson, Task.class);

                taskList.add(task);

                //更新数据库的任务信息
                boolean flag = updateTaskFromDB(task);

                if(flag){
                    //删除缓存数据
                    redisTemplate.opsForZSet().remove(key,taskJson);
                }
            }
        }

        return taskList;
    }

    /**
     * 更新DB数据
     * @param task
     */
    private boolean updateTaskFromDB(Task task) {
        boolean flag = false;
        //更新DB数据
        try {
            //删除任务表记录
            taskinfoMapper.deleteById(task.getTaskId());

            //更新任务日志表
            TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(task.getTaskId());
            taskinfoLogs.setStatus(ScheduleConstants.EXECUTED);
            taskinfoLogsMapper.updateById(taskinfoLogs);

            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return flag;
    }

    /**
     * 同步任务
     *   MySQL->Redis
     *     cron表达式：秒 分 时 日 月 周
     */
    @Scheduled(cron = "0 0/5 * * * ?")
    public void refreshData(){
        //查询任务表的数据（只查询未来5分钟执行的任务）
        QueryWrapper<Taskinfo> queryWrapper = new QueryWrapper<>();
        Date futureDate = DateTime.now().plusMinutes(5).toDate();
        queryWrapper.le("execute_time",futureDate);
        List<Taskinfo> taskinfoList = taskinfoMapper.selectList(queryWrapper);

        //把查询的任务加入到Redis中
        if(CollectionUtils.isNotEmpty(taskinfoList)){
            taskinfoList.forEach(taskinfo -> {
                Task task = BeanHelper.copyProperties(taskinfo,Task.class);
                task.setExecuteTime(taskinfo.getExecuteTime().getTime());
                addTaskToCache(task);
            });
        }
    }
}
